package com.qianbei.view.bluetooth;

import android.annotation.TargetApi;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.widget.Toast;

import com.google.gson.Gson;
import com.qianbei.common.base.MyApplication;
import com.qianbei.common.net.view.MsgAlert;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.UUID;

import cn.hand.blelib.BleBus;

import static com.umeng.socialize.utils.ContextUtil.getPackageName;

/**
 * <pre>
 *     author : pipi
 *     e-mail : xxx@xx
 *     time   : 2018/09/29
 *     desc   : 蓝牙业务处理类(连接逻辑仿google demo)
 *     version: 1.0
 * </pre>
 */

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class BluetoothService {
    private static final String TAG = BluetoothService.class.getSimpleName();
    private static final UUID SMART_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    private ConnectThread mConnectThread;
    private ConnectedThread mConnectedThread;
    private int mState;
    private BluetoothAdapter mAdapter;
    private BluetoothDevice mBluetoothDevice;

    // Constants that indicate the current connection state
    public static final int STATE_NONE = 0;       // we're doing nothing
    public static final int STATE_LISTEN = 1;     // now listening for incoming connections
    public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
    public static final int STATE_CONNECTED = 3;  // now connected to a remote device
    private OnDataListener mOnDataListener;


    public final static UUID SER_UUID = UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb");
    public final static  UUID SPP_UUID = UUID.fromString("0000fff1-0000-1000-8000-00805f9b34fb");
    public final static UUID CONFIG_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");

    public BluetoothGattCharacteristic mNotifyCharacteristic;

    private Context context = MyApplication.mApplication;

    public void setOnDataListener(OnDataListener onDataListener) {
        this.mOnDataListener = onDataListener;
    }

    public interface OnDataListener {
        void onFetchLoadData(String loadData);
        void onLoadDataFail();
    }

    private OnConnectListener mOnConnectListener;

    public void setOnConnectListener(OnConnectListener listener) {
        this.mOnConnectListener = listener;
    }

    public interface OnConnectListener{
        void onConnectSuccess();

        void onConnectFail();
    }



    private OnConnectStatusListener mOnConnectStatusListener;
    public void setOnConnectStatus(OnConnectStatusListener listener) {
        this.mOnConnectStatusListener = listener;
    }

    public interface OnConnectStatusListener{
        void onDisConnSuccess();

    }

    private static class BluetoothServiceHolder {
        private static BluetoothService INSTANCE = new BluetoothService();
    }

    public static BluetoothService getInstance() {
        return BluetoothServiceHolder.INSTANCE;
    }

    public void setContext(Context context){
        this.context = context;
    }

    private BluetoothService() {
        mState = STATE_NONE;
        mAdapter = BluetoothAdapter.getDefaultAdapter();
    }

    public synchronized void stop() {
        if (mConnectedThread != null) {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }
        if (mConnectThread != null) {
            mConnectThread.cancel();
            mConnectThread = null;
        }
    }

    public int getState() {
        return mState;
    }


    /**
     * Start the ConnectThread to initiate a connection to a remote device.
     *
     * @param device The BluetoothDevice to connect
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    public synchronized void connect(BluetoothDevice device) {
        Log.d(TAG, "connect to: " + device);
        mBluetoothDevice = device;
        // Cancel any thread attempting to make a connection
       /* if (mState == STATE_CONNECTING) {
            if (mConnectThread != null) {
                mConnectThread.cancel();
                mConnectThread = null;
            }
        }*/
        if (mConnectThread != null) {
            mConnectThread.cancel();
            mConnectThread = null;
        }
        // Cancel any thread currently running a connection
        if (mConnectedThread != null) {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }
        // Start the thread to connect with the given device
        mConnectThread = new ConnectThread(device);
        mConnectThread.start();
    }

    public void disconnectBlueTooth(){
        if (bluetoothGatt!=null){
            bluetoothGatt.disconnect();
        }
    }

    /**
     * Indicate that the connection attempt failed and notify the UI Activity.
     */
    private void connectionFailed() {
        mState = STATE_NONE;
        if (mOnConnectListener != null) {
            mOnConnectListener.onConnectFail();
        }
//        connect(mBluetoothDevice);
        Log.w(TAG, "connectionFailed");
    }

    public void clearZero(){
        if (mNotifyCharacteristic!=null&&bluetoothGatt!=null) {
            Toast.makeText(context, "clearZero当前BluetoothGattCharacteristic的字节为:"+mNotifyCharacteristic.getValue()==null?"null":mNotifyCharacteristic.getValue()+"", Toast.LENGTH_SHORT).show();
            BleBus.getDefault().doClearZero(mNotifyCharacteristic, bluetoothGatt);
        }
    };

    public void readWeight(){
        if (mNotifyCharacteristic!=null&&bluetoothGatt!=null) {
            Toast.makeText(context, "readWeight当前BluetoothGattCharacteristic的字节为:"+mNotifyCharacteristic.getValue()==null?"null":mNotifyCharacteristic.getValue()+"", Toast.LENGTH_SHORT).show();
            BleBus.getDefault().doReadWeight(mNotifyCharacteristic, bluetoothGatt);
        }
    };
    private BluetoothGattCharacteristic characteristic;
    private BluetoothGatt bluetoothGatt;

    public BluetoothGatt getBluetoothGatt(){

      return bluetoothGatt;
    };

    /**
     * This thread runs while attempting to make an outgoing connection
     * with a device. It runs straight through; the connection either
     * succeeds or fails.
     */
    private class ConnectThread extends Thread {
        private final BluetoothSocket mmSocket;
        private BluetoothDevice mmDevice;


        @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
        public ConnectThread(BluetoothDevice device) {
            mmDevice = device;
            BluetoothSocket tmp = null;
            // Get a BluetoothSocket for a connection with the
            // given BluetoothDevice
            try {
                tmp = device.createRfcommSocketToServiceRecord(SMART_UUID);
            } catch (IOException e) {
                Log.e(TAG, "Socket create failed", e);
            }
            mmSocket = tmp;
            mState = STATE_CONNECTING;
        }

        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
        public void run() {
//            if (mState != BluetoothGatt.STATE_CONNECTED) {
                bluetoothGatt = mmDevice.connectGatt(context, false, new BluetoothGattCallback() {
                    @Override
                    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                        super.onCharacteristicChanged(gatt, characteristic);
                        byte[] value = characteristic.getValue();
                        if (value != null && value.length > 0) {
                            BleBus.getDefault().startAnalyzeData(context,value);
                        }
//                        BluetoothService.getInstance().bluetoothGatt = gatt;
//                        BluetoothService.getInstance().characteristic = characteristic;
                    }

                    @Override
                    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                        super.onCharacteristicRead(gatt, characteristic, status);
//                        gatt.readCharacteristic(characteristic);
//                        Toast.makeText(context,"蓝牙称重设备回调数据读取"+status,Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                        super.onConnectionStateChange(gatt, status, newState);
//                        Toast.makeText(context, status, Toast.LENGTH_SHORT).show();
                        if(newState == BluetoothProfile.STATE_CONNECTED){
                            //连接成功后，发送发现服务请求。
                            mState = BluetoothGatt.STATE_CONNECTED;
                            bluetoothGatt.discoverServices();
                        }else {
                            mState = BluetoothGatt.STATE_DISCONNECTED;
                            bluetoothGatt.close();
                            if (mOnConnectStatusListener!=null) {
                                mOnConnectStatusListener.onDisConnSuccess();
                                bluetoothGatt = null;
                            }
                        }
                    }


                    @Override
                    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                        super.onServicesDiscovered(gatt, status);
                        if(status == BluetoothGatt.GATT_SUCCESS){
                            //发现成功后，则可以通过下面方法获取service 列表。

                            BluetoothGattService gattService = gatt.getService(SER_UUID);
                            BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(SPP_UUID);
                            BluetoothGattDescriptor descriptor = gattCharacteristic.getDescriptor(CONFIG_DESCRIPTOR_UUID);
                            if(descriptor !=null){
                                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                                gatt.writeDescriptor(descriptor);
                            }
                            final int charaProp = gattCharacteristic.getProperties();
                            if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
                                mNotifyCharacteristic = gattCharacteristic;
                                boolean b = gatt.setCharacteristicNotification(gattCharacteristic, true);
                                if (b){
                                    mOnConnectListener.onConnectSuccess();
                                }else {
                                    mOnConnectListener.onConnectFail();
                                }
                            }
                            bluetoothGatt.readCharacteristic(descriptor.getCharacteristic());
                        }
                    }
                });
//                bluetoothGatt.connect();

//            }else {
////                bluetoothGatt.connect();
////                if (mmSocket!=null){
////                    try {
////                        mmSocket.connect();
////                    } catch (IOException e) {
////                        e.printStackTrace();
////                    }
////                };
//            }

            Log.i(TAG, "BEGIN mConnectThread");
            setName("ConnectThread");
            // Always cancel discovery because it will slow down a connection
            if (mAdapter.isDiscovering()) {
                mAdapter.cancelDiscovery();
            }
            // Make a connection to the BluetoothSocket
//            try {
//                // This is a blocking call and will only return on a
//                // successful connection or an exception
//                mmSocket.connect();
//            } catch (IOException e) {
//                // Close the socket
//                try {
//                    mmSocket.close();
//                } catch (IOException e2) {
//                    Log.e(TAG, "unable to close socket during connection failure", e2);
//                }
//                connectionFailed();
//                return;
//            }

            // Reset the ConnectThread because we're done
//            synchronized (BluetoothService.this) {
//                mConnectThread = null;
//            }
            // Start the connected thread
//            connected(mmSocket);
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close of connect socket failed", e);
            }
        }

        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
        public boolean enableNotification(BluetoothGatt gatt, UUID serviceUUID, UUID characteristicUUID) {
            boolean success = false;
            BluetoothGattService service = gatt.getService(serviceUUID);
            if (service != null) {
                BluetoothGattCharacteristic characteristic = findNotifyCharacteristic(service, characteristicUUID);
                if (characteristic != null) {
                    success = gatt.setCharacteristicNotification(characteristic, true);
                    if (success) {
                        // 来源：http://stackoverflow.com/questions/38045294/oncharacteristicchanged-not-called-with-ble
                        for(BluetoothGattDescriptor dp: characteristic.getDescriptors()){
                            if (dp != null) {
                                if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
                                    dp.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                                } else if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
                                    dp.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                                }
                                gatt.writeDescriptor(dp);
                            }
                        }
                    }
                }
            }
            return success;
        }

        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
        private BluetoothGattCharacteristic findNotifyCharacteristic(BluetoothGattService service, UUID characteristicUUID) {
            BluetoothGattCharacteristic characteristic = null;
            List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
            for (BluetoothGattCharacteristic c : characteristics) {
                if ((c.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0
                        && characteristicUUID.equals(c.getUuid())) {
                    characteristic = c;
                    break;
                }
            }
            if (characteristic != null)
                return characteristic;
            for (BluetoothGattCharacteristic c : characteristics) {
                if ((c.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0
                        && characteristicUUID.equals(c.getUuid())) {
                    characteristic = c;
                    break;
                }
            }
            return characteristic;
        }

    }

    /**
     * Start the ConnectedThread to begin managing a Bluetooth connection
     *
     * @param socket The BluetoothSocket on which the connection was made
     */
    public synchronized void connected(BluetoothSocket socket) {
        Log.d(TAG, "connected");

        // Cancel the thread that completed the connection
        if (mConnectThread != null) {
            mConnectThread.cancel();
            mConnectThread = null;
        }
        // Cancel any thread currently running a connection
        if (mConnectedThread != null) {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }
        // Start the thread to manage the connection and perform transmissions
//        if (mOnConnectListener != null) {
//            mOnConnectListener.onConnectSuccess();
//        }
//        mConnectedThread = new ConnectedThread(socket);
//        mConnectedThread.start();
    }

    /**
     * This thread runs during a connection with a remote device.
     * It handles all incoming and outgoing transmissions.
     */
    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;

        public ConnectedThread(BluetoothSocket socket) {
            Log.d(TAG, "create ConnectedThread: ");
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;
            // Get the BluetoothSocket input and output streams
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "temp sockets not created", e);
            }
            mmInStream = tmpIn;
            mmOutStream = tmpOut;
            mState = STATE_CONNECTED;
        }

        public void run() {
            Log.i(TAG, "BEGIN mConnectedThread");
            byte[] buffer = new byte[1024];
            int bytes;
            // Keep listening to the InputStream while connected
            while (mState == STATE_CONNECTED) {
                try {
                    // Read from the InputStream
                    bytes = mmInStream.read(buffer);
                    boolean parseSuccess = parseData(buffer);
                    if (parseSuccess) {
                        String str = Utils.byte2Str(contentBuffer);
                        String content = new String(contentBuffer);
                        Log.e(TAG, "parseData:::" + str);
                        Log.e(TAG, "parseData:content:" + content);
                        if (mOnDataListener != null) {
                            mOnDataListener.onFetchLoadData(content);
                        }
                    } else {
                        if (mOnDataListener != null) {
                            mOnDataListener.onLoadDataFail();
                        }
                    }
                } catch (IOException e) {
                    Log.e(TAG, "disconnected", e);
                    connectionLost();
                    break;
                }
            }
        }

        /**
         * Write to the connected OutStream.
         *
         * @param buffer The bytes to write
         */
        public void write(byte[] buffer) {
            try {
                mmOutStream.write(buffer);
            } catch (IOException e) {
                Log.e(TAG, "Exception during write", e);
            }
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect socket failed", e);
            }
        }
    }

    private void connectionLost() {
        mState = STATE_NONE;
        if (mOnConnectListener != null) {
            Log.w(TAG, "connectionLost");
            mOnConnectListener.onConnectFail();
        }
    }

    /**
     * Write to the ConnectedThread in an unsynchronized manner
     *
     * @param out The bytes to write
     * @see ConnectedThread#write(byte[])
     */
    public void write(byte[] out) {
        // Create temporary object
        ConnectedThread r;
        // Synchronize a copy of the ConnectedThread
        synchronized (this) {
            if (mState != STATE_CONNECTED) return;
            r = mConnectedThread;
        }
        Log.e(TAG, "sendData:" + Utils.byte2Str(out));
        // Perform the write unsynchronized
        r.write(out);
    }

    private byte[] contentBuffer;

    /**
     * 解析数据
     *
     * @param byte_datas
     * @return 解析数据
     */
    public boolean parseData(byte[] byte_datas) {
        if (byte_datas == null || byte_datas.length <= 4) {
            return false;
        }
        char content_length = (char) (((byte_datas[0] & 0xff) << 8) | (byte_datas[1] & 0xff));
        contentBuffer = new byte[content_length];
        for (int i = 0; i < content_length; i++) {
            contentBuffer[i] = byte_datas[2 + i];
        }
        Log.i(TAG, "parseData: "+Utils.byte2Str(contentBuffer));
        byte crc16_h = byte_datas[2 + content_length];
        byte crc16_l = byte_datas[3 + content_length];
        char crc16 = (char) (((crc16_h & 0xff) << 8) | (crc16_l & 0xff));
        char crc16Calc = Utils.crc16Calc(contentBuffer, content_length, Const.CRC_TYPE);
        if (crc16Calc != crc16) {
            Log.w(TAG, "parseData: crc校验失败" );
            return false;
        }
        return true;
    }

    /**
     * 发送数据
     *
     * @param byte_datas 消息体
     * @return 发送状态
     */
    public boolean sendData(byte[] byte_datas) {
        if (byte_datas == null || byte_datas.length == 0) {
            return false;
        }
        byte[] send_datas = new byte[byte_datas.length + 4];
        char data_length = (char) byte_datas.length;
        send_datas[0] = (byte) ((data_length & 0xff00) >> 8);
        send_datas[1] = (byte) (data_length & 0xff);
        for (int i = 0; i < byte_datas.length; i++) {
            send_datas[2 + i] = byte_datas[i];
        }
        char crc16Calc = Utils.crc16Calc(byte_datas, data_length, Const.CRC_TYPE);
        send_datas[2 + data_length] = (byte) ((crc16Calc & 0xff00) >> 8);
        send_datas[3 + data_length] = (byte) (crc16Calc & 0xff);
        write(send_datas);
        return true;
    }

    /**
     * Sends a message.
     *
     * @param message A string of text to send.
     */
    private void sendMessage(String message) {
        // Check that we're actually connected before trying anything
        if (mState != STATE_CONNECTED) {
            new MsgAlert().show("蓝牙连接已经断开");
            return;
        }
        // Check that there's actually something to send
        if (message.length() > 0) {
            // Get the message bytes and tell the BluetoothChatService to write
            final byte[] send = message.getBytes();
            //发送消息的位置
            Log.i(TAG, "sendData::content" + message);
            Log.i(TAG, "sendData::" + Utils.byte2Str(send));
            sendData(send);
        }
    }

    /**
     * 发送载重查询指令
     */
    public void queryLoadData(int sendId) {
        LoadDataQ loadData = new LoadDataQ();
        loadData.setBtName(mAdapter.getName());
        loadData.setTime(Utils.getNowTime());
        loadData.setSendId(sendId);
        LoadDataQuery loadDataQuery = new LoadDataQuery();
        loadDataQuery.setPda_LoadQuiry(loadData);
        Gson gson = new Gson();
        String json = gson.toJson(loadDataQuery);
        sendMessage(json);
    }
}
